package com.ivo.activiti.bpmn;

import com.ivo.activiti.bpmn.record.RecordService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wj
 * @version 1.0
 */
@Service
public class ProcessService {

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;

    @Resource
    private ManagementService managementService;

    @Resource
    private RecordService recordService;

    /**
     * 流程部署
     * @param name 流程图文件名
     * @param bytes 流程图字节
     */
    public void deploy(String name, byte[] bytes) {
        repositoryService.createDeployment().addBytes(name, bytes).deploy();
    }

    /**
     * 获取流程定义列表
     * @return List<ProcessDefinition>
     */
    public List<ProcessDefinition> getProcessDefinitions() {
       return repositoryService.createProcessDefinitionQuery().list();
    }

    /**
     * 启动流程
     * @param key KEY
     */
    public void processStart(String key) {
        Authentication.setAuthenticatedUserId("SYS");
        runtimeService.startProcessInstanceByKey(key, key+new Date().getTime());
    }

    /**
     * 获取流程实例
     * @return List<ProcessInstance>
     */
    public List<ProcessInstance> getProcessInstances() {
        return runtimeService.createProcessInstanceQuery().list();
    }

    /**
     * 获取流程实例的流程图
     * @param processInstanceId 流程实例
     * @return InputStream
     */
    public InputStream  getResourceAsStream(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if(processInstance == null) return null;
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();

        return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());
    }

    /**
     * 高亮显示流程实例的节点、连线
     * @param processInstanceId 流程实例
     * @return Map
     */
    public Map<String, List<String>> historicActivity(String processInstanceId) {
        ProcessInstance processInstance = runtimeService
                .createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if(processInstance == null) return new HashMap<>();

        // 获取流程定义信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        //获取流程历史中已执行节点
        List<HistoricActivityInstance> historyList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstance.getProcessInstanceId())
                .orderByHistoricActivityInstanceId()
                .asc()
                .list();

        // 高亮已执行环节id集合
        List<String> highLightedActivitis = new ArrayList<>();
        for (HistoricActivityInstance activityInstance : historyList) {
            highLightedActivitis.add(activityInstance.getActivityId());
        }

        // 高亮线路id集合
        List<String> highLightedFlows = getHighLightedFlows(bpmnModel, historyList);

        //高亮当前节点
        List<String> highLightedCurrentNode = new ArrayList<>();
        Set<String> currIds = runtimeService
                .createExecutionQuery()
                .processInstanceId(processInstance.getProcessInstanceId()).list()
                .stream().map(Execution::getActivityId).collect(Collectors.toSet());
        for(String currId : currIds) {
            if(StringUtils.isNotEmpty(currId)) highLightedCurrentNode.add(currId);
        }

        Map<String, List<String>> map = new HashMap<>();
        map.put("highLightedActivitis", highLightedActivitis);
        map.put("highLightedFlows", highLightedFlows);
        map.put("highLightedCurrentNode", highLightedCurrentNode);
        return map;
    }

    /**
     * 获取流程实例经过的连线
     * @param bpmnModel BpmnModel
     * @param historicActivityInstances List<HistoricActivityInstance>
     * @return List<String>
     */
    public List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 24小时制
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 用以保存高亮的线flowId
        List<String> highFlows = new ArrayList<>();

        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());
            // 用以保存后续开始时间相同的节点
            List<FlowNode> sameStartTimeNodes = new ArrayList<>();
            FlowNode sameActivityImpl1 = null;
            // 第一个节点
            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);
            HistoricActivityInstance activityImp2_;

            for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
                // 后续第1个节点
                activityImp2_ = historicActivityInstances.get(k);

                // 都是userTask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                if (!(
                        activityImpl_.getActivityType().equals("userTask")
                                && activityImp2_.getActivityType().equals("userTask")
                                && df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))
                )) {
                    // 找到紧跟在后面的一个节点
                    sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());
                    break;
                }
            }
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                // 后续第一个节点
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);
                // 后续第二个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);

                // 如果第一个节点和第二个节点开始时间相同保存
                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {
                    FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {
                    // 有不相同跳出循环
                    break;
                }
            }
            // 取出节点的所有出去的线
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows();

            // 对所有的线进行遍历
            for (SequenceFlow pvmTransition : pvmTransitions) {
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }

    /**
     * 执行任务
     * @param processInstanceId 流程实例
     */
    public void completeTask(String processInstanceId, Map<String, Object> variables) {
        ProcessInstance processInstance = runtimeService
                .createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if(processInstance == null) return;

        List<Task> taskList = taskService
                .createTaskQuery()
                .processInstanceId(processInstance.getProcessInstanceId())
                .active()
                .orderByTaskCreateTime()
                .asc()
                .list();

        if(taskList.size() == 0) {
            runtimeService.deleteProcessInstance(processInstanceId, "任务已全部结束");
            return;
        }
        Task task = taskList.get(0);
        taskService.complete(task.getId(), variables);
    }

    public List<HistoricTaskInstance> getHistoricTasks(String processInstanceId) {
        List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceStartTime()
                .asc()
                .list();
        return taskList;
    }

    public List<HistoricActivityInstance> getHistoricActivitys(String processInstanceId) {
        List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();
        return  activityList;
    }

    public Map getProcessVariables(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if(processInstance == null) return new HashMap<>();
        return processInstance.getProcessVariables();
    }

    public Map getTaskVariables(String processInstanceId) {
        List<Task> taskList = taskService
                .createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .orderByTaskCreateTime()
                .asc()
                .list();
        if(taskList.size() == 0) return new HashMap<>();
        Task task = taskList.get(0);
        return taskService.getVariables(task.getId());
    }

    /**
     * 任务节点跳转
     * @param taskId 当前节点ID
     * @param targetNodeId 跳转的目标节点
     */
    public void taskJump(String taskId, String targetNodeId) {
        managementService.executeCommand(new JumpAnyWhereCmd(taskId, targetNodeId));
        recordService.completeTask(taskId);
    }

    public void rejectTask(String processInstanceId) {
        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list();
        if(taskList.size() == 0) return;
        Task task = taskList.get(0);

        String targetNodeId = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list()
                .get(0)
                .getTaskDefinitionKey();
        taskJump(task.getId(), targetNodeId);
    }
}
